package pub.guoxin.oops.common.base.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import pub.guoxin.oops.vo.ResultVo;
import pub.guoxin.oops.vo.ResultVoBuilder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * GlobalExceptionHandler 全局拦截异常统一处理
 * <p>
 * Created by guoxin on 17-12-17.
 */
@Slf4j
@ControllerAdvice
@RestController
public class GlobalExceptionHandler {

    @Autowired
    private ResultVoBuilder builder;


    /**
     * 主要处理服务错误与参数异常
     */
    @ExceptionHandler(Throwable.class)
    public ResultVo is5xx(/*HttpServletRequest request, HttpServletResponse response, */Throwable ex) {
        ex.printStackTrace();
        return builder.buildServerErrorResultInfo();
    }

    /**
     * 处理参数异常类型错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({
            // 处理参数异常类型错误
            IllegalArgumentException.class,
            // @RequestParam Required String parameter 'closingTime' is not present
            // 使用 @RequestParam(require = true) 参数为null 将其规范到参数错误的阵列中
            MissingServletRequestParameterException.class,
            // Failed to convert value of type 'java.lang.String' to required type 'java.lang.Boolean';
            // 解决参数转换类型错误，将其规范到参数错误的阵列中
            MethodArgumentTypeMismatchException.class,
            //解决 Request method 'GET' not supported 将其规范到参数错误的阵列中
            HttpRequestMethodNotSupportedException.class,
            // 接口参数绑定时，帮顶参数时报错
            BindException.class,
            // @RequestBody对象为空，异常Required request body is missing
            HttpMessageNotReadableException.class
    })
    public ResultVo is4xx(Throwable ex) {
        ex.printStackTrace();
        return builder.buildBadRequestResultInfo(ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVo validHandler(MethodArgumentNotValidException ex) {
        ex.printStackTrace();
        BindingResult bindingResult = ex.getBindingResult();
        String msg = null;
        if (bindingResult.hasErrors()) {
            List<ObjectError> errorList = bindingResult.getAllErrors();
            // 返回最前错误即可，没必要都返回
            msg = errorList.stream().findFirst().map(DefaultMessageSourceResolvable::getDefaultMessage).orElse(msg);
        }
        return builder.buildBadRequestResultInfo(msg);
    }


    /**
     * 主要处理自定义异常
     */
    @ExceptionHandler(InjectUserException.class)
    public ResultVo injectUserHandler(InjectUserException ex) {
        return builder.buildNotFoundResultInfo(ex.getMessage());
    }

    @ExceptionHandler(AuthenticationException.class)
    public ResultVo authenticationHandler(AuthenticationException ex) {
        ex.printStackTrace();
        return builder.buildBadRequestResultInfo("权限异常");
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResultVo accessDenied(Throwable ex) {
        ex.printStackTrace();
        return builder.buildBadRequestResultInfo(ex.getMessage());
    }

}
